using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Threading.Tasks;

namespace CoreManager.Core.Caching
{
    /// <summary>
    /// 基于Redis的缓存服务实现
    /// </summary>
    public class RedisCacheService : ICacheService
    {
        private readonly ConnectionMultiplexer _redis;
        private readonly IDatabase _db;
        private readonly CacheOptions _options;

        public RedisCacheService(ConnectionMultiplexer redis, IOptions<CacheOptions> options)
        {
            _redis = redis ?? throw new ArgumentNullException(nameof(redis));
            _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
            _db = _redis.GetDatabase();
        }

        /// <inheritdoc />
        public T Get<T>(string key)
        {
            var value = _db.StringGet(GetFullKey(key));
            if (value.IsNullOrEmpty)
            {
                return default;
            }

            return JsonConvert.DeserializeObject<T>(value);
        }

        /// <inheritdoc />
        public async Task<T> GetAsync<T>(string key)
        {
            var value = await _db.StringGetAsync(GetFullKey(key));
            if (value.IsNullOrEmpty)
            {
                return default;
            }

            return JsonConvert.DeserializeObject<T>(value);
        }

        /// <inheritdoc />
        public void Set<T>(string key, T value, TimeSpan? expiration = null)
        {
            var serializedValue = JsonConvert.SerializeObject(value);
            _db.StringSet(
                GetFullKey(key),
                serializedValue,
                expiration ?? _options.DefaultExpiration
            );
        }

        /// <inheritdoc />
        public async Task SetAsync<T>(string key, T value, TimeSpan? expiration = null)
        {
            var serializedValue = JsonConvert.SerializeObject(value);
            await _db.StringSetAsync(
                GetFullKey(key),
                serializedValue,
                expiration ?? _options.DefaultExpiration
            );
        }

        /// <inheritdoc />
        public bool Remove(string key)
        {
            return _db.KeyDelete(GetFullKey(key));
        }

        /// <inheritdoc />
        public async Task<bool> RemoveAsync(string key)
        {
            return await _db.KeyDeleteAsync(GetFullKey(key));
        }

        /// <inheritdoc />
        public bool Exists(string key)
        {
            return _db.KeyExists(GetFullKey(key));
        }

        /// <inheritdoc />
        public async Task<bool> ExistsAsync(string key)
        {
            return await _db.KeyExistsAsync(GetFullKey(key));
        }

        /// <inheritdoc />
        public bool Refresh(string key, TimeSpan expiration)
        {
            return _db.KeyExpire(GetFullKey(key), expiration);
        }

        /// <inheritdoc />
        public async Task<bool> RefreshAsync(string key, TimeSpan expiration)
        {
            return await _db.KeyExpireAsync(GetFullKey(key), expiration);
        }

        /// <inheritdoc />
        public void Clear()
        {
            // 清除指定前缀的所有键
            var endpoints = _redis.GetEndPoints();
            foreach (var endpoint in endpoints)
            {
                var server = _redis.GetServer(endpoint);
                var keys = server.Keys(pattern: $"{_options.KeyPrefix}*");
                foreach (var key in keys)
                {
                    _db.KeyDelete(key);
                }
            }
        }

        /// <inheritdoc />
        public async Task ClearAsync()
        {
            // 清除指定前缀的所有键
            var endpoints = _redis.GetEndPoints();
            foreach (var endpoint in endpoints)
            {
                var server = _redis.GetServer(endpoint);
                var keys = server.Keys(pattern: $"{_options.KeyPrefix}*");
                foreach (var key in keys)
                {
                    await _db.KeyDeleteAsync(key);
                }
            }
        }

        /// <summary>
        /// 获取带前缀的完整键名
        /// </summary>
        private string GetFullKey(string key)
        {
            return string.IsNullOrEmpty(_options.KeyPrefix) ? key : $"{_options.KeyPrefix}:{key}";
        }
    }
}